/********************************************************************************
 *      Copyright:  (C) 2022 SCEUC
 *                  All rights reserved.
 *
 *       Filename:  at91_gpio.h
 *    Description:  This head file is GPIO operator functions 
 *
 *        Version:  1.0.0(2022年06月28日)
 *         Author:  Zhang.Kang <13874854275@163.com>
 *      ChangeLog:  1, Release initial version on "2022年06月28日 13时29分37秒"
 *
 ********************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>


#define CFG_GPIO_DIR "/sys/class/gpio"
#define CFG_GPIO_SYS_FILE_EXPORTED_TIME_IN_100MS 10

#define CHECK_GPIO_VALID(x)  if( !(x) || strlen(x)<3) { printf("Invalid input at91 gpio '%s'\n", x); return -1; }

int gpio_is_exported(char *gpio)
{
	char buf[64] = {0};
    char group;
    int  pin;

    CHECK_GPIO_VALID(gpio);

    group = toupper(gpio[1]);
    pin = atoi(&gpio[2]);

	snprintf(buf, sizeof(buf), CFG_GPIO_DIR "/pio%c%d/direction", group, pin);

    if( !access(buf, F_OK) )
        return 0;

	return -1;
}


int gpio_export(char *gpio)
{
	int fd = 0;
	int i;
	char buf[64] = {0};
	size_t len = 0;
    char group;
    int  pin;
    int  number;

    CHECK_GPIO_VALID(gpio);

	if( gpio_is_exported(gpio) == 0 )
	{
		return 0; //No need to re-export
	}

	fd = open(CFG_GPIO_DIR "/export", O_WRONLY);
	if( fd < 0 )
	{
		return -1;
	}

    group = toupper(gpio[1]);
    pin = atoi(&gpio[2]);
    number = (group-'A')*32+pin;

	len = snprintf(buf, sizeof(buf), "%d", number);
	write(fd, buf, len);
	close(fd);

	/* wait until file is actually available in user space */
	for (i = 0; i < CFG_GPIO_SYS_FILE_EXPORTED_TIME_IN_100MS; i++)
	{
		if( gpio_is_exported(gpio) == 0 )
		{
			return 0; //GPIO is present in user space
		}
		usleep(100 * 1000);
	}

	return -1;
}

int gpio_unexport(char *gpio)
{
	int fd = 0;
	char buf[64] = {0};
	size_t len = 0;
    char group;
    int  pin;
    int  number;

    CHECK_GPIO_VALID(gpio);

    group = toupper(gpio[1]);
    pin = atoi(&gpio[2]);
    number = (group-'A')*32+pin;

	fd = open(CFG_GPIO_DIR "/unexport", O_WRONLY);
	if( fd < 0 )
	{
		return -1;
	}

	len = snprintf(buf, sizeof(buf), "%d", number);
	write(fd, buf, len);
	close(fd);

	return 0;
}

int gpio_set_direction(char *gpio, bool output)
{
	int fd = 0;
	char buf[64] = {0};
    char group;
    int  pin;

    CHECK_GPIO_VALID(gpio);

    group = toupper(gpio[1]);
    pin = atoi(&gpio[2]);

    if( gpio_export(gpio)< 0 )
    {
        return -1;
    }

	snprintf(buf, sizeof(buf), CFG_GPIO_DIR "/pio%c%d/direction", group, pin);

	fd = open(buf, O_WRONLY);
	if( fd < 0 )
	{
		return -1;
	}

	if(output)
	{
		write(fd, "out", 3);
	}
	else
	{
		write(fd, "in", 2);
	}

	close(fd);

	return 0;
}

int gpio_set_edge(char *gpio, bool rising, bool falling)
{
	int fd = 0;
	char buf[64] = {0};
    char group;
    int  pin;

    CHECK_GPIO_VALID(gpio);

    if( gpio_set_direction(gpio, 0) < 0 )
    {
        return -1;
    }

    group = toupper(gpio[1]);
    pin = atoi(&gpio[2]);

	snprintf(buf, sizeof(buf), CFG_GPIO_DIR "/pio%c%d/edge", group, pin);

	fd = open(buf, O_WRONLY);
	if( fd < 0 )
	{
		return -1;
	}

	if(rising && falling)
	{
		write(fd, "both", 4);
	}
	else if(rising)
	{
		write(fd, "rising", 6);
	}
	else
	{
		write(fd, "falling", 7);
	}

	close(fd);

	return 0;
}


int at91_gpio_set(char *gpio, int level)
{
	int fd = 0;
	char buf[64] = {0};
    char group;
    int  pin;

    CHECK_GPIO_VALID(gpio);

    gpio_set_direction(gpio, 1);

    group = toupper(gpio[1]);
    pin = atoi(&gpio[2]);

	snprintf(buf, sizeof(buf), CFG_GPIO_DIR "/pio%c%d/value", group, pin);

	fd = open(buf, O_WRONLY | O_NONBLOCK);
	if( fd < 0 )
	{
		return -1;
	}

    if(level)
    {
		write(fd, "1", 1);
    }
    else
    {
		write(fd, "0", 1);
    }

    return 0;
}

int at91_gpio_get(char *gpio)
{
	int fd = 0;
	char buf[64] = {0};
    char group;
    int  pin;

    CHECK_GPIO_VALID(gpio);

    gpio_set_direction(gpio, 0);

    group = toupper(gpio[1]);
    pin = atoi(&gpio[2]);

	snprintf(buf, sizeof(buf), CFG_GPIO_DIR "/pio%c%d/value", group, pin);

	fd = open(buf, O_RDONLY | O_NONBLOCK);
	if( fd < 0 )
	{
		return -1;
	}

    memset( buf, 0, sizeof(buf) );

    if( read(fd, buf, 1) < 0 )
    {
        return -1;
    }

    return atoi(buf);
}
